package org.persistente.manipula.conexao;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.persistente.manipula.mapeamento.Manipulador;
import org.persistente.manipula.mapeamento.MapaObjetoRelacional;

/**
 * Operações com bancos de dados.
 */
public class ConexaoBD {

    /**
     * Fonte de conexão global.
     */
    private static FonteConexaoAbstrata fonteConexao;
    private static ThreadLocal<Connection> conexoes = new ThreadLocal<Connection>();

    /**
     * Altera a fonte de conexão global.
     * @param fonteConexao Fonte de conexão.
     */
    public static void setFonteConexao(FonteConexaoAbstrata fonteConexao) {
        ConexaoBD.fonteConexao = fonteConexao;
    }

    /**
     * Retorna a fonte de conexão global.
     * @return Fonte de conexão global.
     */
    public static FonteConexaoAbstrata getFonteConexao() {
        return fonteConexao;
    }

    /**
     * Cria uma nova conexão com o banco de dados.
     * @return Conexão nova.
     */
    public static Connection novaConexao() {
        return fonteConexao.novaConexao();
    }



    public static Connection abreTransacao(){
        Connection con = novaConexao();
        conexoes.set(con);
        return con;
    }

    public static  void fechaTransacao(){
        Connection con = conexoes.get();
        if(con != null){
            fecha(con);
            conexoes.remove();
        }
    }

    public static Connection iniciaOperacao(){
        Connection con = conexoes.get();
        if(con == null){
            return novaConexao();
        }
        return con;
    }

    public static void terminaOperacao(Connection con){
        if(conexoes.get() == null){
            fecha(con);
        }
    }

    /**
     * Cria uma nova exceção de banco de dados com uma mensagem específica.
     * @param str Mensagem da exceção a ser criada.
     * @return Exceção nova.
     */
    public static ConexaoException novaExcecao(String str) {
        return novaExcecao(new SQLException(str));
    }

    /**
     * Encapsula uma exceção do tipo SQLException em um objeto do tipo ConexaoException.
     * @param e Exceção a ser encapsulada.
     * @return Encapsulamento.
     */
    public static ConexaoException novaExcecao(SQLException e) {
        return new ConexaoException("Erro ao realizar operação no banco de dados.", e);
    }

    /**
     * Fecha um statement.
     * @param st Statement a ser fechado.
     */
    public static void fecha(Statement st) {
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Fecha um result set.
     * @param rs Result set a ser fechado.
     */
    public static void fecha(ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Fecha uma conexão com o banco de dados.
     * @param con Conexão a ser fechada.
     */
    public static void fecha(Connection con) {
        if (con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Cria um manipulador para uma determinada entidade.
     * @param <T> Tipo de entidade que será manipulada.
     * @param mapa Mapa objeto relacional classe/entidade.
     * @return Novo manipulador.
     */
    public static <T> Manipulador<T> criaManipulador(MapaObjetoRelacional mapa) {
        return criaManipulador(mapa, null);
    }

    /**
     * Cria um manipulador para uma determinada entidade.
     * @param <T> Tipo de entidade que será manipulada.
     * @param mapa Mapa objeto relacional classe/entidade.
     * @param conexao Conexão com um banco de dados a ser usada pelo manipulador.
     * @return Novo manipulador.
     */
    public static <T> Manipulador<T> criaManipulador(MapaObjetoRelacional mapa, Connection conexao) {
        return fonteConexao.getDriverBanco().criaManipulador(mapa, conexao);
    }
}
